package com.dimenlink.utils;

import com.dimenlink.entity.config.AppConfig;
import com.dimenlink.entity.constants.Constants;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.File;
import java.math.BigDecimal;

@Component
public class FFmpegUtils {

    @Resource
    private AppConfig appConfig;

    /**
     * 创建缩略图
     *
     * @param filePath
     */
    public void createImageThumbnail(String filePath) {
        String CMD = "ffmpeg -i \"%s\" -vf scale=200:-1 \"%s\"";
        CMD = String.format(CMD, filePath, filePath+Constants.IMAGE_THUMBNAIL_SUFFIX);
        ProcessUtils.executeCommand(CMD,appConfig.getShowFFmpegLog());

    }

    /**
     * 获取视频时长
     *
     * @param completeVideo
     * @return
     */
    public Integer getVideoInfoDuration(String completeVideo) {
        // 定义 ffprobe 命令模板，用于获取视频时长信息
        // -v error：屏蔽错误日志以外的输出
        // -show_entries format=duration：仅显示格式中的时长字段
        // -of default=noprint_wrappers=1:nokey=1：输出格式去掉包装器、去掉字段名，直接返回值
        final String CMD_GET_CODE = "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 \"%s\"";

        // 将视频文件路径填入命令模板，生成完整的 ffprobe 执行命令
        String cmd = String.format(CMD_GET_CODE, completeVideo);

        // 执行命令并获取结果，appConfig.getShowFFmpegLog() 控制是否显示 FFmpeg 相关日志
        String result = ProcessUtils.executeCommand(cmd, appConfig.getShowFFmpegLog());

        // 如果命令执行结果为空，返回 0（表示获取时长失败）
        if (StringTools.isEmpty(result)) {
            return 0;
        }

        // 去除结果中的换行符（避免因换行影响后续转换）
        result = result.replace("\n", "");

        // 将字符串形式的时长（如 "123.45"）转为 BigDecimal，再取整为 Integer 返回
        return new BigDecimal(result).intValue();
    }

    /**
     * 获取视频编码格式
     *
     * @param videoFilePath
     * @return
     */
    public String getVideoCodec(String videoFilePath) {
        // 定义 ffprobe 命令模板，用于获取视频流的编码格式信息
        // -v error：屏蔽错误日志，只输出关键内容
        // -select_streams v:0：仅选择第 1 个视频流（v 表示视频流，0 是索引）
        // -show_entries stream=codec_name：仅显示流的编码名称字段
        final String CMD_GET_CODE = "ffprobe -v error -select_streams v:0 -show_entries stream=codec_name \"%s\"";

        // 将视频文件路径填入命令模板，生成完整的 ffprobe 执行命令
        String cmd = String.format(CMD_GET_CODE, videoFilePath);

        // 执行命令并获取结果，appConfig.getShowFFmpegLog() 控制是否显示 FFmpeg 相关日志
        String result = ProcessUtils.executeCommand(cmd, appConfig.getShowFFmpegLog());

        // 去除结果中的换行符（避免因换行影响后续字符串处理）
        result = result.replace("\n", "");

        // 截取等号（=）之后的内容，假设命令输出格式类似 "codec_name=h264"
        result = result.substring(result.indexOf("=") + 1);

        // 截取第一个 "[" 之前的内容（处理可能带括号的额外信息，如有些输出可能是 "h264[...]"）
        //String codec = result.substring(0, result.indexOf("["));

        //上面写法如果输出中不包含 [ 字符，indexOf("[") 会返回 -1，导致 substring(0, -1) 出错。更安全的写法应该是：
        int bracketIndex = result.indexOf("[");
        String codec = bracketIndex >= 0 ? result.substring(0, bracketIndex) : result;

        // 返回提取到的编码格式（如 h264、vp9 等）
        return codec;
    }

    /**
     * 将 HEVC（H.265）编码的视频转换为 H.264 编码（libx264）
     *
     * @param newFileName
     * @param videoFilePath
     */
    public void convertHevc2Mp4(String newFileName, String videoFilePath) {
        // 定义 FFmpeg 命令模板，用于将 HEVC（H.265）编码的视频转换为 H.264 编码（libx264）
        // -i %s：输入文件路径，对应 videoFilePath
        // -c:v libx264：视频编码器指定为 libx264（将视频转为 H.264 编码）
        // -crf 20：恒定速率因子，数值越小质量越高（20 属于中等偏高质量，可根据需求调整）
        // %s：输出文件路径，对应 newFileName
        final String CMD_HEVC_264 = "ffmpeg -i \"%s\" -c:v libx264 -crf 20 \"%s\" -y";

        // 将输入参数（输出文件名、输入文件路径）填充到命令模板中，生成完整的 FFmpeg 命令
        String cmd = String.format(CMD_HEVC_264, videoFilePath, newFileName);

        // 执行 FFmpeg 命令，并根据 appConfig.getShowFFmpegLog() 的配置决定是否显示 FFmpeg 的日志
        // ProcessUtils.executeCommand：需确保该工具类实现了“执行系统命令并处理输出/日志”的逻辑
        ProcessUtils.executeCommand(cmd, appConfig.getShowFFmpegLog());
    }

    /**
     * 将视频文件转换为 TS 格式
     *
     * @param tsFolder
     * @param videoFilePath
     */
    public void convertVideo2Ts(File tsFolder, String videoFilePath) {
        // 定义 FFmpeg 命令模板 1：将原始视频转为 TS 格式
        // -y：覆盖已存在的输出文件
        // -i "%s"：输入文件路径（第一个 %s ）
        // -vcodec copy：直接复制视频流，不重新编码
        // -acodec copy：直接复制音频流，不重新编码
        // -vbsf h264_mp4toannexb：H.264 流格式转换（适配 TS 容器要求）
        // "%s"：输出 TS 文件路径（第二个 %s ）
        final String CMD_TRANSFER_2TS = "ffmpeg -y -i \"%s\" -vcodec copy -acodec copy -vbsf h264_mp4toannexb \"%s\"";

        // 定义 FFmpeg 命令模板 2：切割 TS 文件并生成 M3U8 索引
        // -i "%s"：输入 TS 文件路径（第一个 %s ）
        // -c copy：直接复制流（不重新编码）
        // -map 0：选择所有输入流
        // -f segment：启用分片模式
        // -segment_list "%s"：生成的 M3U8 索引文件路径（第二个 %s ）
        // -segment_time 10：每个分片时长为 10 秒
        // %s/%%4d.ts：分片文件路径格式（第三个 %s 是输出目录，%%4d 是 4 位序号）
        final String CMD_CUT_TS = "ffmpeg -i \"%s\" -c copy -map 0 -f segment -segment_list \"%s\" -segment_time 10 %s/%%4d.ts";

        // 拼接 TS 文件的完整路径：tsFolder 目录 + TS 文件名（从 Constants.TS_NAME 获取）
        String tsPath = tsFolder + "/" + Constants.TS_NAME;

        // ========== 第一步：将原始视频转为 TS 格式 ==========
        // 填充命令模板，生成完整 FFmpeg 命令（输入是原视频路径，输出是 tsPath ）
        String cmd = String.format(CMD_TRANSFER_2TS, videoFilePath, tsPath);
        // 执行命令，根据配置决定是否显示 FFmpeg 日志
        ProcessUtils.executeCommand(cmd, appConfig.getShowFFmpegLog());

        // ========== 第二步：切割 TS 并生成 M3U8 索引 ==========
        // 拼接 M3U8 文件路径：tsFolder 目录 + M3U8 文件名（从 Constants.M3U8_NAME 获取）
        String m3u8Path = tsFolder.getPath() + "/" + Constants.M3U8_NAME;
        // 填充命令模板，生成切割命令（输入是 tsPath ，输出索引是 m3u8Path ，分片输出到 tsFolder 目录 ）
        cmd = String.format(CMD_CUT_TS, tsPath, m3u8Path, tsFolder.getPath());
        // 执行命令，根据配置决定是否显示 FFmpeg 日志
        ProcessUtils.executeCommand(cmd, appConfig.getShowFFmpegLog());

        // ========== 第三步：删除临时 TS 文件 ==========
        // 删除第一步生成的完整 TS 文件（因为已经切割成分片，原始大 TS 文件不再需要）
        new File(tsPath).delete();
    }
}
